![1195-replCalculator.jpg](https://www.red-gate.com/simple-talk/wp-content/uploads/imported/1195-replCalculator.jpg)
Microsoft’s .NET 4 framework introduces the ‘dynamic’ keyword in C#, which, as the name suggests, finally brings new ‘dynamic’ features to the programming language. These dynamic features bring several advantages, and the one which I’ll focus on here is the possibility of integrating C# with other dynamic languages, such as IronPython.
Why bother with dynamic features? To show why by illustration, I’m supplying, with this article, a simple application that does a graphical XY plot of the functions that you enter.
Now we Can Have the Best of both worlds
I’ve been playing around with this new functionality, and I’ll start off by presenting two simple code samples in this article to illustrate how this feature can be used, and then explain how they can be built upon. The first sample calls Python methods from within C#, and the latter calls C# methods from within Python. Whilst the demonstrations are perhaps not very useful in themselves, they give you a start, I then show how this technique could form the basis of an application that takes advantage of the best features of both languages by providing a more extensive example that finally does something useful: plots out any function you choose, or type in, graphically.
Specifically, I’ll be demonstrating how the use of the dynamic keyword makes it incredibly easy to build a cross-language calculator capable of solving some basic mathematical expressions. Building on that, I’ll give you a brief overview of a more elegant way of combing (or perhaps hybridizing) the two languages, as well as how to extend the basic calculator engine into a read-eval-print loop (REPL), or ‘interactive top-level’ . This can be used for a number of very cool applications (See Ben Hall’s IronRuby REPL to extend SQL Data Generator and his ‘Methodist’ add-in For Net Reflector). I’ll use this REPL to evaluate more sophisticated functions for a wide range of values, in the graph-plotter.
To run the examples, you need to have Visual Studio 2010 with .NET 4.0 and Iron Python 2.6 for .NET 4.0 installed on your computer.
Calling Python Methods From C#
This first example demonstrates how to access a Python class called Calculator from within C# code. When I originally started writing these demonstrations, the Python class was loaded from an external file, and the resulting program was just a very simple calculator, and these are the steps I went through to build it. However, I later built on this platform and build a function plotter, and the end result of my tinkering was to actually have the Python class loaded and executed in Memory. Once I’ve walked you through the foundation, I’ll go on to elaborate on the final application.
Create the Python Method
This is relevant if you decide you want to keep your python class in a separate file. On the other hand, if you’d prefer to follow my example (which is can be downloaded from the top of this article) and run the python within memory, you can ignore this section and skip further down the article:
- Start Visual Studio 2010 and create a new Console Application project with the name DynamicPy;
- In the Solution Explorer, right-click the DynamicPy project file, and select Add > New Item > Text file;
- In the Solution Explorer, rename the new file to calc.py;
- Open calc.py, and define the Calculator class in Python:
12345class Calculator:def add(self, argA, argB):return argA+argBdef sub(self, argA, argB):return argA-argB
- Save the file.
Call the Python Method from C#
Now that you’ve got your Python class ready to go, you need to actually get your C# code to use it. The code below (with the exception of the actual call to the Python class) is relevant to the final implementation I’ll be demonstrating later, although the actual execution will change slightly. Nevertheless, if you’ve used an external Python class, here’s what you need to do:
- In your project, add references to the following DLLs, found in Iron Python’s installation directory (e.g. C:\Program Files (x86)\IronPython 2.6 for .NET 4.0):
- IronPython.dll
- IronPython.Modules.dll
- IronPython.Dynamic.dll
- Microsoft.Scripting.dll
- Microsoft.Scripting.Debuging.dll
- Open Program.cs and add:
1using IronPython.Hosting;
- In the Main method, create the Python engine object:
1var engine = Python.CreateEngine();
- Load the Python script file using the dynamic keyword. This will resolve the object’s operations at runtime:
1dynamic py = engine.ExecuteFile( @"C:\MyProjects\DynamicPy\DynamicPy\calc.py");
- Create an instance of the Python Calculator class:
1dynamic calc = py.Calculator();
- Write some code to perform the desired calculations using the calc object.
The resulting program.cs file might look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
using System; using IronPython.Hosting; namespace DynamicPy { class Program { static void Main(string[] args) { var engine = Python.CreateEngine(); dynamic py = engine.ExecuteFile( @"C:\MyProjects\DynamicPy\DynamicPy\calc.py"); dynamic calc = py.Calculator(); Console.WriteLine(calc.__class__.__name__); //write 'Calculator' double a = 7.5; double b = 2.5; double res; res = calc.add(a, b); Console.WriteLine("{0} + {1} = {2}", a, b, res); res = calc.sub(a, b); Console.WriteLine("{0} - {1} = {2}", a, b, res); Console.WriteLine("Press any key to finish..."); Console.ReadLine(); } } } |
At this point, you can save, build and run the application. The .NET 4 executable performs the calculations by calling the calc object, which in turn resolves the result of the Calculator Python class, and performs our simple calculation:
Figure 1. The basic calculator.
While this method works perfectly well for basic calculations, I decided that it lacks elegance (and not a little functionality). So, in a moment, I will demonstrate how I did away with this external file altogether, fully encapsulated the python class within my C#, and executed it from within memory. However, before we go into that, I’ll illustrate how the basic interaction between C# and IronPython can flow in the opposite direction.
Calling C# Methods from Python
In the following example, I’ll perform the reverse operation; creating the calculator’s class in C# and then accessing it from Python. There is no attached demonstration for this, and it does not significantly inform the final build of the application, so this is primarily an academic exercise.
Create the C# Class
- In Visual Studio, create a new C# Class Library project with the name DynamicCS;
- Create a standard C# class in the DynamicCS project by renaming the existing Class1.cs class to Calculator.cs. Implement two simple methods for adding and subtracting numbers, as follows:
12345678910111213141516using System;namespace DynamicCS{public class Calculator{public double add(double argA, double argB){return argA + argB;}public double sub(double argA, double argB){return argA - argB;}}}
- In the Solution Explorer, right-click the DynamicCS project file, and select Add > New Item > Class;
- In the Solution Explorer, rename the new file to DynamicCalc.cs;
- In the newly-created class, add:
1using System.Dynamic
- Define a public DynamicCalc class that derives from DynamicObject:
1public class DynamicCalc: DynamicObject
- Implement the constructor of the DynamicCalc class:
1234public DynamicCalc(){calc = new Calculator();}
- Override methods from the DynamicObject class. In the presented example, the TryGetMember method has to be overridden, at the very least:
123public override bool TryGetMember(GetMemberBinder binder, out object result){} - The resulting DynamicCalc.cs file might look like this:
123456789101112131415161718192021222324252627namespace DynamicCS{public class DynamicCalc: DynamicObject{Calculator calc;public DynamicCalc(){calc = new Calculator();}public override bool TryGetMember(GetMemberBinder binder, out object result){result = null;switch (binder.Name){case "add":result = (Func<double, double ,double>) ((double a, double b)=> calc.add(a, b));return true;case "sub":result = (Func<double, double, double>) ((double a, double b)=> calc.sub(a, b));return true;}return false;}}}
Note that, in the TryGetMember method:
- binder.Name represents a name of the method or property called;
- result is an output value of the method or property. In this case, it is a delegate to the ‘add’ and ‘sub’ methods of the calc object;
- return should return ‘true’ if the operation is successful, otherwise ‘false’.
Finally, save and build the solution. The Python executable performs the calculations by calling the calc object, which in turn resolves the result of the C# DynamicCalc class, imported from DynamicCS.dll.
Call the C# Methods from Python
- In the Solution Explorer, right click the DynamicCS project file, and select Add > New Item > Text file;
- In the Solution Explorer, rename the new file to client.py;
- Add an import sys statement, pointing to the directory where your C# DLL is located. In this example, I have saved DynamicCS.dllin C:\MyProjects\DynamicCS\DynamicCS\bin\Debug:
12import syssys.path.append(r"C:\MyProjects\DynamicCS\DynamicCS\bin\Debug")
- Reference your C# DLL:
12import clrclr.AddReference(r"DynamicCS.dll")
- Create a Python object as you would for any other object, then use it to perform some calculations:
12from DynamicCS import DynamicCalccalc=DynamicCalc()
- The resulting client.py file might look like this:
1234567891011121314151617181920212223import syssys.path.append(r"C:\MyProjects\DynamicCS\DynamicCS\bin\Debug")import clrclr.AddReference(r"DynamicCS.dll")from DynamicCS import DynamicCalccalc=DynamicCalc()print calc.__class__.__name__# display the name of the class: 'DynamicCalc'a=7.5b=2.5res = calc.add(a, b)print a, '+', b, '=', resres = calc.sub(a, b)print a, '-', b, '=', resraw_input('Press any key to finish...')
- Save and run the Python client.
Figure 2. The basic calculator, implemented mostly in Python.Creating and calling Python methods in C#
The Python executable performs the calculations by calling the calc object, which in turn resolves the result of the C# DynamicCalc class, imported from DynamicCS.dll. The end result looks much the same as the earlier implementation.
Create the Python Class Directly in C# code
Working again with C# as the primary language, this is where things start to get a bit more interesting. Given that the python class is so simple and compact, I decided to use the dynamic keyword in a more creative way, and embed the python directly in the C# code:
- Start Visual Studio 2010 and create a new Console Application project with the name DynamicPyMem
- In your project, add references to the following DLLs, found in Iron Python’s installation directory (e.g. C:\Program Files (x86)\IronPython 2.6 for .NET 4.0):
- IronPython.dll
- IronPython.Modules.dll
- Microsoft.Dynamic.dll
- Microsoft.Scripting.dll
- Microsoft.Scripting.Debugging.dll
- Open Program.cs and add:
123using IronPython.Hosting;using Microsoft.Scripting;using Microsoft.Scripting.Hosting;
- In the Main method, create the Python script as a string:
12345678var statements = new StringBuilder();statements.AppendLine(@"import math");statements.AppendLine(@"class Calculator:");statements.AppendLine(@" def add(self, argA, argB):");statements.AppendLine(@" return argA+argB");statements.AppendLine(@" def sub(self, argA, argB):");statements.AppendLine(@" return argA+argB");
- In the Main method, create the Python engine and scope objects, and then execute the source:
1234567// Make the text a python sourceScriptEngine engine = Python.CreateEngine();engine.CreateScriptSourceFromString(statements.ToString(), SourceCodeKind.Statements);dynamic pyScope = engine.CreateScope();// Execute the python sourceengine.Execute(statements.ToString(), pyScope);
- Create an instance of the Python Calculator class:
1dynamic calc = pyScope.Calculator();
- Write some code to perform some calculations using the calc object:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748The resulting program.cs file might look like this:using System;using System.Text;using IronPython.Hosting;using Microsoft.Scripting;using Microsoft.Scripting.Hosting; namespace DynamicPyMem{class Program{static void Main(string[] args){// Build the python script as a stringvar statements = new StringBuilder();statements.AppendLine(@"import math");statements.AppendLine(@"class Calculator:");statements.AppendLine(@" def add(self, argA, argB):");statements.AppendLine(@" return argA+argB");statements.AppendLine(@" def sub(self, argA, argB):");statements.AppendLine(@" return argA+argB");// Convert the text into a python sourceScriptEngine engine = Python.CreateEngine();engine.CreateScriptSourceFromString(statements.ToString(), SourceCodeKind.Statements);dynamic pyScope = engine.CreateScope();// Execute the python sourceengine.Execute(statements.ToString(), pyScope);// Create an instance of Calculator in C#dynamic calc = pyScope.Calculator();Console.WriteLine(calc.__class__.__name__); //'Calculator'double a = 7.5;double b = 2.5;double res;res = calc.add(a, b);Console.WriteLine("{0} + {1} = {2}", a, b, res);res = calc.sub(a, b);Console.WriteLine("{0} - {1} = {2}", a, b, res);Console.WriteLine("Press any key to finish...");Console.ReadLine();}}}
- Save, build and run the application
So, having created this neat and smooth solution, the question is “What can we do with this?” For my part, I decided to bolt on a few bits and pieces, and turn the whole thing into a function plotter, rather than just a calculator. As an immediate result, the maths module changed somewhat, given that I now needed to evaluate expressions, rather than solve a single calculation. Thus, my Python engine now looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// Build the python script as a string var statements = new StringBuilder(); statements.AppendLine(@"import math"); statements.AppendLine(@"from math import *"); // Include all constant parameters statements.AppendLine(textBoxParameters.Text); // Define python Calculator class statements.AppendLine(@"class calculator"); // Define python expression as function of x statements.AppendLine(@" def expr(self, x):"); statements.AppendLine(@" return " + textBoxFunction.Text); // Convert the text into python source code ScriptEngine engine = Python.CreateEngine(); engine.CreateScriptSourceFromString(statements.ToString(), SourceCodeKind.Statements); // Execute the python source dynamic pyScope = engine.CreateScope(); engine.Execute(statements.ToString(), pyScope); //Create an instance of the python Calculator class dynamic calc = pyScope.Calculator(); // Call calc methods // ... |
I don’t need to go into detail about the code behind the plotting functionality since you have the source code to look at. I’ve added components for calculating X and Y scales, and evaluating users’ expressions (entered as text) for about 500 values of X. In addition, the users may also supply parameters with which to populate their expression, which is significantly easier to deal with dynamically. Finally, the results of the evaluated functions are then plotted on a graph, meaning that I’ve also had to add this to the solution:
1 2 |
using System.Drawing; using System.Drawing.Drawing2D;; |
The Plot Thickens
As you’ll see when you try out the finished Plot Function application, that plotting happens remarkably quickly, illustrating just how efficient the use of Python is in this case. As a counterpoint, achieving the same evaluations of a user-supplied expression in pure C# is… cumbersome.
In the same way as in the earlier example, the .NET 4 executable performs the calculations by calling the calc object, which in turn resolves the result of the Calculator Python class. Python’s math module allows for the evaluation of very complex expressions; instead of the simple adding or subtracting of numbers, the program may calculate more complex mathematical expressions, such as 5*sin(pi/4)/(pi/4). Moreover, if the expression is defined as a function of x, it can have a general form, such as A*sin(x)/x (where A is a constant parameter), and it can then be evaluated for every value of x. Additionally, because the python program is just text, using it to create an intelligent expression evaluator is a neat example of applying python in C#.
Please feel free to download the attached example Plot function solution, which allows for the plotting of any function of x which is entered as text by the user (and which is acceptable by python) – i.e. the solution is not limited to a prefined set of functions. Take a look at the screenshot below for a quick demo of how to use it:
Figure 3. The function plotter and its controls
Final Thoughts
So, in this article, we’ve seen a simple way of how the ‘dynamic’ keyword in C# can be used to integrate .NET code with Python, as well as way of embedding the necessary python code directly in the C# code. This is just the result of a little tinkering I’ve done, but hopefully this will give you some ideas of how to draw on the benefits offered by other .NET programming languages when most appropriate.
Load comments